package com.livery.liu.dao.ibatis;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.util.Assert;
import com.livery.liu.util.Page;

/**
 * IBatis Dao的泛型基类.
 * <p>
 * 继承于Spring的SqlMapClientDaoSupport,提供分页函数和若干便捷查询方法，并对返回值作了泛型类型转换.
 * </p>
 * <pre class="code">
 *  
 * </pre>
 *
 * @author  <a href="mailto:livery.liu@gmail.com"> livery liu</a>
 * @see SqlMapClientDaoSupport
 */
@SuppressWarnings("unchecked")
public class IBatisGenericDaoTemplate extends SqlMapClientDaoSupport {

	public static final String POSTFIX_SAVE = ".save";

	public static final String POSTFIX_UPDATE = ".update";

	public static final String POSTFIX_DELETE = ".delete";

	public static final String POSTFIX_DELETE_BYPROPERTY = ".deletebyproperty";

	public static final String POSTFIX_GETBYID = ".getbyid";

	public static final String POSTFIX_GETALL = ".getall";
	
	public static final String POSTFIX_DELETE_BYUPDATE = ".deletebyupdate";
	
	public static final String POSTFIX_GETLISTBYMAP = ".getlistbymap";
	
	public static final String POSTFIX_GETLISTBYPO = ".selectbypo";

	public static final String POSTFIX_GETUNIQUEBY = ".getuniqueby";
	
	public static final String POSTFIX_GETUNIQUEBYMAP = ".getuniquebymap";

	/**
	 * 根据ID获取对象
	 */
	public <T> T getById(Class<T> entityClass, Serializable id) {
		T po = (T) getSqlMapClientTemplate().queryForObject(entityClass.getName() + POSTFIX_GETBYID, id);
		if (po == null)
			throw new ObjectRetrievalFailureException(entityClass, id);
		return po;
	}

	/**
	 * 新增对象
	 */
	public <T> T save(T po) {
		return (T)getSqlMapClientTemplate().insert(po.getClass().getName() + POSTFIX_SAVE, po);
	}

	/**
	 * 保存对象
	 */
	public <T> int update(T po) {
		return getSqlMapClientTemplate().update(po.getClass().getName() + POSTFIX_UPDATE, po);
	}

	/**
	 * 删除对象
	 */
	public <T> int deleteByProperty(T po) {
		return getSqlMapClientTemplate().delete(po.getClass().getName() + POSTFIX_DELETE_BYPROPERTY, po);
	}

	/**
	 * 根据ID删除对象
	 */
	public <T> int delete(Class<T> entityClass, Serializable id) {
		return getSqlMapClientTemplate().delete(entityClass.getName()+POSTFIX_DELETE, id);
	}

	/**
	 * 更新字段代表删除
	 * @param <T>
	 * @param entityClass
	 * @param id
	 * @return
	 */
	public <T> int deleteByUpdate(Class<T> entityClass,Serializable id){
		return getSqlMapClientTemplate().update(entityClass.getName()+POSTFIX_DELETE_BYUPDATE, id);
	}

	/**
	 * 获取全部对象(全部)
	 * @param <T>
	 * @param entityClass
	 * @return
	 */
	public <T> List<T> getAll(Class<T> entityClass) {
		return getSqlMapClientTemplate().queryForList(entityClass.getName() + POSTFIX_GETALL,null);
	}
	
	/**
	 * 根据map中的key、value查询list
	 * @param map 包含各种属性的查询
	 */
	public <T> List<T> getList(Class<T> entityClass, Map map) {
		if (map == null)
			return getSqlMapClientTemplate().queryForList(entityClass.getName() + POSTFIX_GETALL, null);
		else {
			return getSqlMapClientTemplate().queryForList(entityClass.getName() + POSTFIX_GETLISTBYMAP, map);
		}
	}

	/**
	 * 根据po的属性得到list集合
	 * @param <T>
	 * @param po
	 * @return
	 */
	public <T> List<T> getList(T po) {
		return getSqlMapClientTemplate().queryForList(po.getClass().getName() + POSTFIX_GETLISTBYPO, po);
	}

	/**
	 * 根据属性名和属性值查询对象.
	 * @return 符合条件的唯一对象
	 */
	public <T> T getUniqueBy(T po) {
		return (T) getSqlMapClientTemplate().queryForObject(po.getClass().getName() + POSTFIX_GETUNIQUEBY, po);
	}

	/**
	 * 根据属性名和属性值查询对象.
	 * @return 符合条件的唯一对象
	 */
	public <T> T getUniqueBy(Class<T> entityClass, String[] name, Object[] value) {
		Assert.notEmpty(name);
		Assert.notEmpty(value);		
		Assert.state(name.length!=value.length);
		try {
			Map<String,Object> map = new HashMap<String,Object>();
			for(int i = 0;i<name.length;i++){
				map.put(name[i], value[i]);
			}
			return (T) getSqlMapClientTemplate().queryForObject(entityClass.getName() + POSTFIX_GETUNIQUEBYMAP, map);
		} catch (Exception e) {
			logger.error("Error when propertie on entity," + e.getMessage(), e.getCause());
			return null;
		}

	}

	/**
	 * 判断对象某些属性的值在数据库中不存在重复
	 * 
	 * @param tableName 数据表名字
	 * @param names	 在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password" 
	 */
	public boolean isNotUnique(Object entity, String tableName, String names) {
		try {
			String primarykey;
			Connection con = getSqlMapClient().getCurrentConnection();
			ResultSet dbMetaData = con.getMetaData().getPrimaryKeys(con.getCatalog(), null, tableName);
			dbMetaData.next();
			if (dbMetaData.getRow() > 0) {
				primarykey = dbMetaData.getString(4);
				if (names.indexOf(primarykey) > -1)
					return false;
			} else {
				return true;
			}

		} catch (SQLException e) {
			logger.error(e.getMessage(), e);
			return false;
		}
		return false;
	}
	
	/**
	 * 分页查询语句<p>
	 * 需要传入查询语句和同样条件的查询数量的语句
	 * @param <T> 泛型结果
	 * @param querySql 查询语句 （命名空间+sql名称）
	 * @param queryCountSql 同样条件的查询数量的语句 （命名空间+sql名称）
	 * @param params 查询参数组合
	 * @param pageNo >=1 当前页数
	 * @param pageSize 每页的记录数量
	 * @return 分页结果
	 */
	public <T> Page<T> getPageBySqlmap(String querySql, String queryCountSql, Map<String,Object> params, int pageNo, int pageSize) {
		
		Assert.isTrue(pageNo >= 1, "pageNo should start from 1");
		// 计算记录总数
		Integer totalCount = (Integer)getSqlMapClientTemplate().queryForObject(queryCountSql, params);
		// 如果没有数据则返回Empty Page
		Assert.notNull(totalCount, "totalCount Error");
		if (totalCount.intValue() == 0) {
			return new Page<T>();
		}
		int totalPageCount = 0;
		int startIndex = 0;
		Assert.isTrue(pageSize > 0, "pageSize should bigger than 0");
		// 计算总页数
		totalPageCount = (totalCount + pageSize -1)/pageSize;
		
		// 计算skip数量
		pageNo = (pageNo>totalPageCount?totalPageCount:pageNo);
		startIndex = (pageNo - 1) * pageSize + 1;
		
		params.put("begin", startIndex);
		params.put("end", startIndex + pageSize-1);
		
		List<T> list = getSqlMapClientTemplate().queryForList(querySql, params);
		return new Page<T>(startIndex, totalCount, pageSize, list);
	} 
	
}
